#pragma once
#include <memory>
#include <iostream>
#include "mytcp.hpp"
#include <unistd.h>
#include <sys/select.h>
class SelectServer
{
    const static int size = sizeof(fd_set) * 8;
public:
    SelectServer(int port)
        : _listensockfd(std::make_unique<TcpSocket>()), _isrunning(false)
    {
        _listensockfd->BuildListenMethod(port);
        // 把辅助数组初始化并且将listenfd放在第一个
        for (int i = 0; i < size; i++)
        {
            _fd_array[i] = -1;
        }
        _fd_array[0] = _listensockfd->Fd();
        //std::cout<<"_listenfd="<<_listensockfd->Fd()<<std::endl;
    }
    void Accepter()
    {
        Addr client;
        int fd = _listensockfd->Accept(&client);
        if (fd >= 0)
        {
            LOG(LogLevel::DEBUG) << "new link,fd=:" << fd<<",client is"<<client.GetAddr();
        }
        //将收到的fd放到辅助数组中
        int pos=0;
        for(;pos<size;pos++)
        {
            //此时辅助数组还没有满,所以可以直接跳出循环然后加入该fd
            if(_fd_array[pos]==-1)
            {
                break;
            }
        }
        if(pos==size)
        {
            //满了,则将该文件描述符关闭
            close(fd);
        }
        else
        {
            //辅助数组没有满,加入该fd
            _fd_array[pos]=fd;
        }
    }
    void Recver(int fd,int i)
    {
        char buffer[1024];
        int n=recv(fd,buffer,sizeof(buffer)-1,0);
        if(n>0)
        {
            //正常读
            buffer[n-1]=0;
            LOG(LogLevel::DEBUG)<<"client says@"<<buffer;
        }
        else if(n==0)
        {
            //此时客户端退出
            LOG(LogLevel::DEBUG)<<"client quit...";
            //不关心该fd并且关闭该fd
            _fd_array[i]=-1;
            close(fd);
        }
        else
        {
            //recv韩束出错
            LOG(LogLevel::FATAL)<<"recv error...";
            //不关心该fd并且关闭该fd
            _fd_array[i]=-1;
            close(fd);
        }
    }
    void Dispatcher(fd_set& readfd)
    {
        //std::cout<<"进入dispatcher逻辑块"<<std::endl;
        for (int i = 0; i < size; i++)
        {
            if (_fd_array[i] == -1)
            {
                continue;
            }
            if (FD_ISSET(_fd_array[i], &readfd))
            {
                // 该fd存在于数组中
                if (_fd_array[i] == _listensockfd->Fd())
                {
                    //将该fd加入辅助数组中
                    //std::cout<<"找到listenfd,进入accepter逻辑块"<<std::endl;
                    Accepter();
                }
                else
                {
                    // 关心读事件
                    //std::cout<<"进入recver逻辑块"<<std::endl;
                    Recver(_fd_array[i],i);
                }
            }
            // else
            // {
            //     std::cout<<"不存在_fd_array["<<i<<"]"<<std::endl;
            // }
        }
        // 开始accept
        //  int fd=_listensockfd->Accept();
        //  if(fd>=0)
        //  {
        //      LOG(LogLevel::DEBUG)<<"new link,fd=:"<<fd;
        //  }
    }
    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            // listen关心读事件
            fd_set readfd;
            FD_ZERO(&readfd);
            //struct timeval timeout = {2, 0};
            // 此时我们就可以使用循环找出数组中最大的fd,并且将数组中的fd设置到fd_set中
            int maxfd = -1;
            for (int i = 0; i < size; i++)
            {
                if (_fd_array[i] == -1) // 不存在该fd
                {
                    continue;
                }
                if (_fd_array[i] > maxfd)
                {
                    maxfd = _fd_array[i];
                }
                FD_SET(_fd_array[i], &readfd);
            }
            // if(FD_ISSET(_listensockfd->Fd(),&readfd))
            // {
            //     std::cout<<"readfd中存在_listenfd"<<std::endl;
            // }
            // else
            // {
            //     std::cout<<"readfd中不存在_listenfd"<<std::endl;
            // }
            int n = select(maxfd + 1, &readfd, nullptr, nullptr, nullptr);
            // int n=select(_listensockfd->Fd()+1,readfd,nullptr,timeout);
            switch (n)
            {
            case -1:
                LOG(LogLevel::ERROR) << "select error!";
                break;
            case 0:
                LOG(LogLevel::DEBUG) << "wait overtime!";
                break;
            default:
                //LOG(LogLevel::DEBUG) << "有事件就绪,n=" << n;
                // 开始处理事件
                Dispatcher(readfd);
                break;
            }
        }
        _isrunning=false;
    }
    void Stop()
    {
        _isrunning = false;
    }
    ~SelectServer()
    {
    }

private:
    std::unique_ptr<Socket> _listensockfd;
    bool _isrunning;
    // 因为select之后,原来fd_set中的值就会改变,所以需要一个辅助数组记录
    int _fd_array[size];
};